package plus.easydo.starter.oauth.server.exception;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import plus.easydo.core.result.DataResult;
import plus.easydo.starter.oauth.core.exception.CustomizeOAuth2Exception;

/**
 * 自定义 WebResponseExceptionTranslator异常捕获
 *
 * @author yuzhanfeng
 */
public class CustomizeOauth2WebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {

    private static final String UNSUPPORTED_GRANT_TYPE = "Unsupported grant type";
    private static final String BAD_CREDENTIALS = "Bad credentials";
    private final ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) {
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e);
        Exception ase = (Exception) this.throwableAnalyzer.getFirstThrowableOfType(Exception.class, causeChain);

        //内部认证服务异常
        if (ase instanceof InternalAuthenticationServiceException) {
            return this.handleOauth2Exception(new CustomizeOAuth2Exception("内部认证服务异常:" + e.getMessage()));
        }

        //无效客户端异常
        if (ase instanceof UnsupportedGrantTypeException) {
            return this.handleOauth2Exception(new CustomizeOAuth2Exception("不支持的验证方式", ase));
        }

        //grantType相关异常
        if (ase instanceof InvalidGrantException) {
            String msg = ase.getMessage();
            if (msg.contains(UNSUPPORTED_GRANT_TYPE)) {
                return this.handleOauth2Exception(new CustomizeOAuth2Exception("不支持的验证方式", ase));
            }
            if (msg.contains(BAD_CREDENTIALS)) {
                return this.handleOauth2Exception(new CustomizeOAuth2Exception("账号或密码错误", ase));
            }
            return this.handleOauth2Exception(new CustomizeOAuth2Exception("请求类型或参数错误:" + e.getMessage(), ase));
        }

        //异常链中包含拒绝访问异常
        if (ase instanceof AccessDeniedException) {

            return this.handleOauth2Exception(new CustomizeOAuth2Exception("拒绝被访问:" + e.getMessage(), ase));
        }

        //抽象验证异常
        if (ase instanceof AuthenticationException) {
            return this.handleOauth2Exception(new CustomizeOAuth2Exception("验证异常:" + e.getMessage()));
        }

        //异常链中有其他OAuth2Exception异常
        if (ase instanceof OAuth2Exception) {
            return this.handleOauth2Exception((OAuth2Exception) ase);
        }

        //异常链中包含Http方法请求异常
        if (ase instanceof HttpRequestMethodNotSupportedException) {
            return this.handleOauth2Exception(new CustomizeOAuth2Exception(ase.getMessage(), ase));
        }
        return this.handleOauth2Exception(new CustomizeOAuth2Exception(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), ase));
    }

    @SuppressWarnings(value = {"rawtypes"})
    private ResponseEntity<OAuth2Exception> handleOauth2Exception(OAuth2Exception e) {
        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json;charset=UTF-8");
        if (status == HttpStatus.UNAUTHORIZED.value() || e instanceof InsufficientScopeException) {
            headers.set("WWW-Authenticate", String.format("%s %s", "Bearer", e.getSummary()));
        }

        return new ResponseEntity(DataResult.fail(status, e.getMessage()), headers, HttpStatus.valueOf(status));
    }


}
